/**
 * 
 */
package denaris.core.entity;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.Sprite;

import denaris.core.AbstractDenarisMap;
import denaris.core.BulletSystem;
import denaris.core.controller.AbstractController;
import denaris.core.listener.ICollisionListener;

/**
 * <p>
 * Entities are objects that fly around. Their position is maintained by an
 * AbstractController.
 * </p>
 * <p>
 * Each Entity has an AbstractDenarisMap attached to it which represents the map
 * in which it is moving. It also provides information about other Entities
 * </p>
 * <p>
 * The Sprite member of this classes Controller finally represents the
 * "physical" object that is drawn to the screen. Entities are always Sprites
 * and never vector objects (these would be Particles).
 * </p>
 * 
 * @author stkl23
 * 
 */
public abstract class AbstractEntity {
	/** the object that controlls the movement and animation of this entity */
	protected AbstractController controller = null;

	/** the map that hosts this entity */
	protected AbstractDenarisMap parentMap = null;

	/** the instance that is to be notified of collision events */
	protected ICollisionListener collisionListener = null;

	/**
	 * The primary bullet system. Classes that override AbstractEntity are
	 * responsible for drawing this BulletSystem as only they know if they
	 * really have one. That way we save a null-test every frame. The same goes
	 * for updating this object
	 */
	protected BulletSystem primary = null;
	
	private boolean tagged = false;
	
	/** inicates the number of points that the player gets when he destroys this entity */
	protected int score = 0;

	public AbstractEntity(AbstractDenarisMap parentMap, int score) {
		this.parentMap = parentMap;
		controller = createController();
		setScore(score);
	}

	public AbstractController getController() {
		return controller;
	}

	public void update() {
		if (controller != null && !controller.advance()) {
			onScopeLeft();
		}
	}

	public AbstractDenarisMap getParentMap() {
		return parentMap;
	}

	public void setParentMap(AbstractDenarisMap parentMap) {
		this.parentMap = parentMap;
	}

	public void checkCollision() {
	}

	protected boolean collidesWith(Sprite s, boolean pixelLevel) {
		return controller.getSprite().collidesWith(s, pixelLevel);
	}

	public void paint(Graphics g) {
		Sprite sprite = controller.getSprite();
		// paint the sprite
		sprite.paint(g);
		if (tagged) {
			g.setColor(0xFF0000);
			g.drawRect(sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight());
		}
	}

	public abstract AbstractController createController();

	public ICollisionListener getCollisionListener() {
		return collisionListener;
	}

	public void setCollisionListener(ICollisionListener collisionListener) {
		this.collisionListener = collisionListener;
	}

	/**
	 * Notify the ICollisionListener that we collided with an opponent.
	 * 
	 * @param opponent
	 */
	protected void notifyCollision(AbstractEntity opponent) {
		if (collisionListener != null) {
			collisionListener.onCollision(this, opponent);
		}
	}

	/**
	 * Notify the ICollisionListener that we collided with a map.
	 * 
	 * @param map
	 */
	protected void notifyCollision(AbstractDenarisMap map) {
		if (collisionListener != null) {
			collisionListener.onCollision(this, map);
		}
	}

	public BulletSystem getPrimaryBulletSystem() {
		return primary;
	}

	public void setPrimaryBulletSystem(BulletSystem system) {
		primary = system;
	}

	public void setController(AbstractController controller) {
		this.controller = controller;
	}

	public Explosion explode(int childExplosions, int frameDelay) {
		Sprite sprite = controller.getSprite();
		int x = sprite.getX();
		int y = sprite.getY();
		return new Explosion(getParentMap(), x, y, childExplosions, frameDelay,	false);
	}

	/** this method is called when the entity goes out of scope */
	public abstract void onScopeLeft();
	
	/** returns the distance of this object relative to the given coordinates*/
	public double getDistance(int x, int y) {
		Sprite sprite = getController().getSprite();
		int dx = sprite.getX() - x;
		int dy = sprite.getY() - y;
		return Math.sqrt(dx * dx + dy * dy);
	}

	public boolean isTagged() {
		return tagged;
	}

	public void setTagged(boolean tagged) {
		this.tagged = tagged;
	}

	public int getScore() {
		return score;
	}

	public void setScore(int score) {
		this.score = score;
	}
	
	public void scrollVertical(int dy) {
		controller.move(0, dy);
		BulletSystem primaryBulletSystem = getPrimaryBulletSystem();
		if (primaryBulletSystem != null) {
			primaryBulletSystem.scrollVertical(dy);
		}
	}
}
